// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) Huawei Technologies Co., Ltd. 2022. All rights reserved.
 * Author: Huawei OS Kernel Lab
 * Create: Mon Dec 26 15:46:11 2022
*/

#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <liblinux/pal.h>
#include <trace/hooks/liblinux.h>
#include <linux/fcntl.h>

#include "firmware.h"


static int fw_read_file_contents(struct fw_priv *fw_priv, const char *path,
				 unsigned int size)
{
	unsigned int rsize;
	char *buf = NULL;
	int rc;

	if (size == 0)
		return -EINVAL;

	buf = vmalloc(size);
	if (!buf)
		return -ENOMEM;

	rc = liblinux_pal_fw_acquire_contents(path, buf, size, &rsize);
	if (rc)
		goto fail;

	if (rsize != size) {
		rc = -EIO;
		goto fail;
	}

	fw_priv->data = buf;
	fw_priv->size = size;
	return 0;
fail:
	vfree(buf);
	return rc;
}

static void
hook_fw_get_filesystem_firmware(void *__data, struct device *device,
				struct fw_priv *fw_priv, const char *suffix,
				int (*decompress)(struct device *dev,
						  struct fw_priv *fw_priv,
						  size_t in_size,
						  const void *in_buffer),
				const char * const *fw_path,
				size_t fw_path_size, int *ret)
{
	int i, len;
	int rc = -ENOENT;
	char *path;
	unsigned int rsize;

	if (decompress != NULL) {
		*ret = -EOPNOTSUPP;
		return;
	}
	path = vmalloc(PATH_MAX);
	if (!path) {
		*ret = -ENOMEM;
		return;
	}

	for (i = 0; i < fw_path_size; i++) {
		/* skip the unset customized path */
		if (!fw_path[i][0])
			continue;

		len = snprintf(path, PATH_MAX, "%s/%s%s",
			       fw_path[i], fw_priv->fw_name, suffix);
		if (len >= PATH_MAX) {
			rc = -ENAMETOOLONG;
			break;
		}

		rc = liblinux_pal_fw_acquire_contents(path, NULL, 0, &rsize);
		if (rc)
			continue;

		rc = fw_read_file_contents(fw_priv, path, rsize);
		if (rc)
			dev_dbg(device, "firmware, attempted to load %s, but failed\n", path);
		else
			break;
	}
	vfree(path);

	if (!rc)
		fw_state_done(fw_priv);

	*ret = rc;
}
INIT_VENDOR_HOOK(ldk_vh_fw_get_filesystem_firmware, hook_fw_get_filesystem_firmware);
